package com.hwxm.ltim.service;

import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hwxm.ltim.entity.EmailVerificationCode;
import com.hwxm.ltim.mapper.EmailVerificationCodeMapper;
import com.hwxm.ltim.util.EmailUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * 邮箱验证码服务
 */
@Slf4j
@Service
public class EmailVerificationCodeService {

    @Autowired
    private EmailVerificationCodeMapper emailVerificationCodeMapper;

    @Autowired
    private EmailUtil emailUtil;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 验证码有效期（分钟）
     */
    private static final int CODE_EXPIRE_MINUTES = 5;

    /**
     * 发送间隔（秒）
     */
    private static final int SEND_INTERVAL_SECONDS = 60;

    /**
     * 发送验证码
     */
    public void sendVerificationCode(String email, Integer type) {
        // 验证邮箱格式
        if (!isValidEmail(email)) {
            throw new RuntimeException("邮箱格式不正确");
        }

        // 检查发送频率限制
        checkSendFrequency(email);

        // 生成6位数字验证码
        String code = RandomUtil.randomNumbers(6);

        // 保存验证码到数据库
        saveVerificationCode(email, code, type);

        // 发送邮件
        try {
            emailUtil.sendVerificationCode(email, code, type);
            log.info("验证码发送成功，邮箱：{}，类型：{}", email, type);
        } catch (Exception e) {
            log.error("邮件发送失败，邮箱：{}，类型：{}", email, type, e);
            throw new RuntimeException("邮件发送失败：" + e.getMessage());
        }

        // 设置发送频率限制
        setSendFrequencyLimit(email);
    }

    /**
     * 验证验证码
     */
    public boolean verifyCode(String email, String code, Integer type) {
        LambdaQueryWrapper<EmailVerificationCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EmailVerificationCode::getEmail, email)
                .eq(EmailVerificationCode::getCode, code)
                .eq(EmailVerificationCode::getType, type)
                .eq(EmailVerificationCode::getStatus, 0) // 未使用
                .gt(EmailVerificationCode::getExpireTime, LocalDateTime.now()) // 未过期
                .orderByDesc(EmailVerificationCode::getCreateTime)
                .last("limit 1");

        EmailVerificationCode verificationCode = emailVerificationCodeMapper.selectOne(wrapper);

        if (verificationCode == null) {
            log.warn("验证码验证失败，邮箱：{}，验证码：{}，类型：{}", email, code, type);
            return false;
        }

        // 标记验证码为已使用
        verificationCode.setStatus(1);
        verificationCode.setUsedTime(LocalDateTime.now());
        emailVerificationCodeMapper.updateById(verificationCode);

        log.info("验证码验证成功，邮箱：{}，类型：{}", email, type);
        return true;
    }

    /**
     * 验证邮箱注册
     */
    public boolean verifyEmailRegister(String email, String code) {
        return verifyCode(email, code, 1);
    }

    /**
     * 验证邮箱登录
     */
    public boolean verifyEmailLogin(String email, String code) {
        return verifyCode(email, code, 2);
    }

    /**
     * 验证密码重置
     */
    public boolean verifyPasswordReset(String email, String code) {
        return verifyCode(email, code, 3);
    }

    /**
     * 验证邮箱修改
     */
    public boolean verifyEmailChange(String email, String code) {
        return verifyCode(email, code, 4);
    }

    /**
     * 保存验证码到数据库
     */
    private void saveVerificationCode(String email, String code, Integer type) {
        // 先将该邮箱同类型的未使用验证码标记为过期
        LambdaQueryWrapper<EmailVerificationCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(EmailVerificationCode::getEmail, email)
                .eq(EmailVerificationCode::getType, type)
                .eq(EmailVerificationCode::getStatus, 0);

        EmailVerificationCode updateCode = new EmailVerificationCode();
        updateCode.setStatus(2); // 已过期
        emailVerificationCodeMapper.update(updateCode, wrapper);

        // 保存新的验证码
        EmailVerificationCode verificationCode = new EmailVerificationCode();
        verificationCode.setEmail(email);
        verificationCode.setCode(code);
        verificationCode.setType(type);
        verificationCode.setStatus(0); // 未使用
        verificationCode.setExpireTime(LocalDateTime.now().plusMinutes(CODE_EXPIRE_MINUTES));
        verificationCode.setCreateTime(LocalDateTime.now());

        emailVerificationCodeMapper.insert(verificationCode);
    }

    /**
     * 检查发送频率限制
     */
    private void checkSendFrequency(String email) {
        try {
            String key = "email_send_limit:" + email;
            Boolean hasKey = redisTemplate.hasKey(key);
            if (Boolean.TRUE.equals(hasKey)) {
                throw new RuntimeException("发送过于频繁，请" + SEND_INTERVAL_SECONDS + "秒后再试");
            }
        } catch (Exception e) {
            log.warn("Redis 连接异常，跳过频率检查：{}", e.getMessage());
            // Redis 异常时不进行频率检查，允许发送
        }
    }

    /**
     * 设置发送频率限制
     */
    private void setSendFrequencyLimit(String email) {
        try {
            String key = "email_send_limit:" + email;
            redisTemplate.opsForValue().set(key, "1", SEND_INTERVAL_SECONDS, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.warn("Redis 连接异常，跳过频率限制设置：{}", e.getMessage());
            // Redis 异常时不设置频率限制
        }
    }

    /**
     * 验证邮箱格式
     */
    private boolean isValidEmail(String email) {
        if (email == null || email.trim().isEmpty()) {
            return false;
        }
        // 简单的邮箱格式验证
        String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
        return email.matches(emailRegex);
    }

    /**
     * 清理过期的验证码
     */
    public void cleanExpiredCodes() {
        LambdaQueryWrapper<EmailVerificationCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(EmailVerificationCode::getExpireTime, LocalDateTime.now())
                .eq(EmailVerificationCode::getStatus, 0);

        EmailVerificationCode updateCode = new EmailVerificationCode();
        updateCode.setStatus(2); // 已过期
        int count = emailVerificationCodeMapper.update(updateCode, wrapper);

        log.info("清理过期验证码，数量：{}", count);
    }
}
